package amazing.core.server;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;

import amazing.core.logging.LogLevel;
import amazing.core.logging.Logger;

public class ServerCore {
	
	public final int port;
	protected Properties properties;
	private ServerSocket server;
	
	private Thread clientAccept;
	private AcceptThread threadAcceptObject;
	
	private ThreadState runAcceptThread = ThreadState.Stopped;
	
	public ServerCore(Properties p)
	{
		properties = p;
		port = Integer.parseInt(p.getProperty("port"));
	}
	
	public void start() throws IOException, InterruptedException
	{
		if (server != null && !server.isClosed())
		{
			Logger.log(LogLevel.Moderate, "Server was already started before, restarting instead.");
			server.close();
		}
		if (clientAccept != null && clientAccept.isAlive())
		{
			Logger.log(LogLevel.Moderate, "Client accept thread is already running, restarting client accept thread...");
			stop();
		}
		Logger.log("Starting server...");
		server = new ServerSocket(port);
		
		loadModules();
		
		clientAccept = new Thread(threadAcceptObject = new AcceptThread());
		clientAccept.setDaemon(false);
		runAcceptThread = ThreadState.Running;
		clientAccept.start();
	}
	private void loadModules()
	{
		String[] directories = properties.getProperty("modulesDirectories").split(";");
		for (String dir : directories)
		{
			String fname = "";
			if (dir == null)
				continue;
			File[] directory = new File(dir).listFiles();
			if (directory == null)
				continue;
			for (File file : directory)
			{
				fname = file.getAbsolutePath();
				int idx = fname.lastIndexOf(".");
				if (idx < 0)
					continue;
				String ext = fname.substring(idx+1);
				// TODO: implement class loading
				switch (ext)
				{
				case "class":
				case "jar":
				}
			}
		}
	}
	public void pause() throws InterruptedException
	{
		synchronized (clientAccept)
		{
			clientAccept.wait();
			runAcceptThread = ThreadState.Paused;
		}
	}
	
	public void resume() throws InterruptedException
	{
		synchronized (clientAccept)
		{
			runAcceptThread = ThreadState.Running;
			clientAccept.notify();
		}
	}
	public void stop() throws InterruptedException, IOException
	{
		synchronized (clientAccept)
		{
			runAcceptThread = ThreadState.Stopped;
			clientAccept.join();
		}
		server.close();
	}
	
	private class AcceptThread implements Runnable
	{
		private List<Thread> clientThreads = new ArrayList<>();
		public void run()
		{
			Logger.log("Starting client accept thread...");
			while (!server.isClosed() && runAcceptThread != ThreadState.Stopped)
			{
				try {
					Socket client = server.accept();
					Logger.log("Accepting client [" + client.getRemoteSocketAddress()+ "]...");
					Thread cli = new Thread(new ClientHandler(client));
					clientThreads.add(cli);
					cli.start();
				} catch (IOException e) {
					Logger.log(LogLevel.High, e.getMessage());
				}
				
			}
		}
		public void remove(Thread sock)
		{
			clientThreads.remove(sock);
		}
	}
	
	protected class ClientHandler implements Runnable
	{
		private Socket client;
		
		public ClientHandler(Socket sock)
		{
			client = sock;
		}
		public void run()
		{
			try
			{
				Logger.log("Started handler for client [" + client.getRemoteSocketAddress() + "]...");
				while (client.isConnected())
				{
					throw new IOException();
				}			
			} catch (IOException e) {
				Logger.log(LogLevel.Moderate, e.getMessage());
			}
			finally
			{
				threadAcceptObject.remove(Thread.currentThread());
				try {
					client.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					Logger.log(LogLevel.Moderate, "Could not close client: " + e.getMessage());
				}
			}
			
		}
	}
}
